#include <limits>
#include <cmath>

#include <gmpxx.h>

#include "segment.h"

segment::segment(): a(point()), b(point())
{
}

segment::segment(const point& a, const point& b): a(a), b(b)
{
}

int segment::left_turn(const point& p) const
{
    int result = left_turn_epsilon(p);

    if (result == 0)
        return left_turn_gmp(p);
    else
        return result;
}

int segment::further(const point& p1, const point& p2) const
{
    int result = further_epsilon(p1, p2);

    if (result == 0)
        return further_gmp(p1, p2);
    else
        return result;
}

int segment::left_turn_epsilon(const point& p) const
{
    double d = (p.x - a.x) * (b.y - a.y) - (p.y - a.y) * (b.x - a.x);
    
    const double meps = std::numeric_limits<double>::epsilon() / 2;
    double eps = 8 * meps * (std::abs((p.x - a.x) * (b.y - a.y)) + std::abs((p.y - a.y) * (b.x - a.x)));

    if (d > eps)
        return 1;
    else if (d < -eps)
        return -1;
    else
        return 0;
}

int segment::left_turn_gmp(const point& p) const
{
    mpf_class d = (mpf_class(p.x) - mpf_class(a.x)) * (mpf_class(b.y) - mpf_class(a.y)) - (mpf_class(p.y) - mpf_class(a.y)) * (mpf_class(b.x) - mpf_class(a.x));

    if (d > 0)
        return 1;
    else if (d < 0)
        return -1;
    else
        return 0;
}

int segment::further_epsilon(const point& p1, const point& p2) const
{
    double d = (b.x - a.x) * (p2.y - p1.y) - (b.y - a.y) * (p2.x - p1.x);

    const double meps = std::numeric_limits<double>::epsilon() / 2;
    double eps = 8 * meps * (std::abs((b.x - a.x) * (p2.y - p1.y)) + std::abs((b.y - a.y) * (p2.x - p1.x)));

    if (d > eps)
        return 1;
    else if (d < -eps)
        return -1;
    else
        return 0;
}

int segment::further_gmp(const point& p1, const point& p2) const
{
    mpf_class d = (mpf_class(b.x) - mpf_class(a.x)) * (mpf_class(p2.y) - mpf_class(p1.y)) - (mpf_class(b.y) - mpf_class(a.y)) * (mpf_class(p2.x) - mpf_class(p1.x));

    if (d > 0)
        return 1;
    else if (d < 0)
        return -1;
    else
        return 0;
}
